Переменные

Переменная — это именованная область памяти, предназначенная для хранения данных определенного типа. Во время выполнения программы значение переменной можно изменять. Все переменные, используемые в программе, должны быть описаны явным образом. При описании для каждой переменной задаются ее имя и тип.

Пример описания целой переменной с именем a и вещественной переменной x:

int a;
float x;

Имя переменной служит для обращения к области памяти, в которой хранится значение переменной. Имя дает программист. Тип переменной выбирается, исходя из диапазона и требуемой точности представления данных. При объявлении можно присвоить переменной некоторое начальное значение, то есть инициализировать ее, например:

int a, b = 1;
float x = 0.1, y = 0.1f;

Здесь описаны:

• переменная a типа int, начальное значение которой не присваивается;

• переменная b типа int, ее начальное значение равно 1;

• переменные х и y типа float, которым присвоены одинаковые начальные значения 0.1. Разница между ними состоит в том, что для инициализации переменной х сначала формируется константа типа double, а затем она преобразуется к типу float ; переменной y значение 0.1 присваивается без промежуточного преобразования.

Рекомендуется всегда инициализировать переменные при описании. При инициализации можно использовать не только константу, но и выражение — главное, чтобы на момент описания оно было вычисляемым, например:

int b = 1, a = 100;
int x = b * a + 25;

Программа на C# состоит из классов, внутри которых описывают методы и данные. Структуру программы иллюстрирует рис. 3.1. Переменные, описанные непосредственно внутри класса, называются полями класса. Им автоматически присваивается так называемое "значение по умолчанию" — как правило, это 0 соответствующего типа. Переменные, описанные внутри метода класса, называются локальными переменными. Их инициализация возлагается на программиста.

Рис. 3.1. Структура программы


Так называемая область действия переменной, то есть область программы, где можно использовать переменную, начинается в точке ее описания и длится до конца блока, внутри которого она описана. Блок — это код, заключенный в фигурные скобки. Основное назначение блока — группировка операторов. В C# любая переменная описана внутри какого-либо блока: класса, метода или блока внутри метода. Имя переменной должно быть уникальным в области ее действия. Область действия распространяется на вложенные в метод блоки.


class X    // начало описания класса X
{
   int A;      // поле A класса X
   int B;      // поле B класса X

   void Y()    // ----------------------------------------- метод Y класса Х
   {
      int C;      // локальная переменная C, область действия – метод Y
      int A;      // локальная переменная A (НЕ конфликтует с полем А)

      {           // ============ вложенный блок 1 ============
         int D;      // локальная переменная D, область действия – этот блок
//       int А;         недопустимо! Ошибка компиляции, конфликт с локальной
//                      переменной А
         С = B;      // присваивание переменной С поля В класса Х (**) 
         С = this.A; // присваивание переменной С поля А класса Х (***)
      }           // ============ конец блока 1 ===============

      {           // ============ вложенный блок 2 ============
         int D;      // локальная переменная D, область действия – этот блок
      }           // ============ конец блока 2 ===============

   }           // ------------------------- конец описания метода Y класса X

}          // конец описания класса X


В листинге 3.1 приведен пример программы, в которой описываются и выводятся на экран локальные переменные.


using System;

namespace Example_1
{
    class Program
    {
        static void Main(string[] args)
        {
            bool b = true, b1 = false;
            int i = 20, i1 = -26;
            double d = .1, d1 = 5.89, d2 = 1.2E1;
            char c = '0', c1 = '\x30';
            string s = "", s1 = "New\nYear";
            Console.WriteLine("code b = {0}, b1 = {1}", b, b1);
            Console.WriteLine("code i = {0}, i1 = {1}", i, i1);
            Console.WriteLine("code d = {0}, d1 = {1}, d2 = {2}", d, d1, d2);
            Console.WriteLine("code c = {0}, c1 = {1}", (int)c, c1);
            Console.WriteLine("code s = {0}, s1 = {1}", s, s1);
        }
    }
}

Листинг 3.1. Описание переменных


Внимание

Переменные создаются при входе в их область действия (блок) и уничтожаются при выходе. Это означает, что после выхода из блока значение переменной не сохраняется. При повторном входе в этот же блок переменная создается заново.

Именованные константы

Можно запретить изменять значение переменной, задав при ее описании ключевое слово const, например:

const int b = 1;
// const распространяется на обе переменные
const float x = 0.1, y = 0.1f;

Такие величины называют именованными константами, или просто константами. Они применяются для того, чтобы вместо значений констант можно было использовать в программе их имена. Это делает программу более понятной и облегчает внесение в нее изменений. Именованные константы должны обязательно инициализироваться при описании, например:

const int b = 1, a = 100;
const int x = b * a + 25;

Операции и выражения

Выражение — это правило вычисления значения. В выражении участвуют операнды, объединенные знаками операций. Операндами простейшего выражения могут быть константы, переменные и вызовы функций.

Например, a + 2 — это выражение, в котором + является знаком операции, а a и 2 — операндами. Пробелы внутри знака операции, состоящей из нескольких символов, не допускаются. Операции C# приведены в таблице 3.1.


Таблица 3.1. Операции C#

Категория

Знак операции

Название

Первичные

x()

Вызов метода или делегата

x[]

Доступ к элементу

x++

Постфиксный инкремент

x--

Постфиксный декремент

new

Выделение памяти

typeof

Получение типа

checked

Проверяемый код

unchecked

Непроверяемый код

Унарные

+

Унарный плюс

-

Унарный минус (арифметическое отрицание)

!

Логическое отрицание

~

Поразрядное отрицание

++x

Префиксный инкремент

--x

Префиксный декремент

(тип)x

Преобразование типа

Мультипликативные (типа умножения)

*

Умножение

/

Деление

%

Остаток от деления

Аддитивные (типа сложения)

+

Сложение

-

Вычитание

Сдвига

<<

Сдвиг влево

>>

Сдвиг вправо

Отношения и проверки типа

<

Меньше

>

Больше

<=

Меньше или равно

>=

Больше или равно

is

Проверка принадлежности типу

as

Приведение типа

Проверки на равенство

==

Равно

!=

Не равно

Поразрядные логические

&

Поразрядная конъюнкция (И)

^

Поразрядное исключающее ИЛИ

|

Поразрядная дизъюнкция (ИЛИ)

Условные логические

&&

Логическое И

||

Логическое ИЛИ

Условная

?:

Условная операция

Присваивания

=

Присваивание

*=

Умножение с присваиванием

/=

Деление с присваиванием

%=

Остаток от деления с присваиванием

+=

Сложение с присваиванием

-=

Вычитание с присваиванием

<<=

Сдвиг влево с присваиванием

>>=

Сдвиг вправо с присваиванием

&=

Поразрядное И с присваиванием

^=

Поразрядное исключающее ИЛИ с присваиванием

|=

Поразрядное ИЛИ с присваиванием


Операции в выражении выполняются в определенном порядке в соответствии с приоритетами, как и в математике. В таблице 3.1 операции расположены по убыванию приоритетов, уровни приоритетов разделены в таблице горизонтальными линиями.

Результат вычисления выражения характеризуется значением и типом. Например, пусть a и b — переменные целого типа и описаны так:

int a = 2, b = 5;

Тогда выражение

a + b имеет значение 7 и тип int,

а выражение

a = b имеет значение, равное помещенному в переменную b (в данном случае — 5) и тип, совпадающий с типом этой переменной.

Если в одном выражении соседствует несколько операций одинакового приоритета, операции присваивания и условная операция выполняются справа налево, остальные — слева направо. Для изменения порядка выполнения операций используются круглые скобки, уровень их вложенности практически не ограничен.

Например,

a + b + c

означает (a + b) + c, a

a = b = c

означает a = (b = c).

Преобразования встроенных арифметических типов-значений

При вычислении выражений может возникнуть необходимость в преобразовании типов. Если операнды, входящие в выражение, одного типа, и операция для этого типа определена, то результат выражения будет иметь тот же тип.

Если операнды разного типа и (или) операция для этого типа не определена, перед вычислениями автоматически выполняется преобразование типа по правилам, обеспечивающим приведение более коротких типов к более длинным для сохранения значимости и точности. Автоматическое (неявное) преобразование возможно не всегда, а только если при этом не может случиться потеря значимости.

Если неявного преобразования из одного типа в другой не существует, программист может задать явное преобразование типа с помощью операции

(тип)x

Его результат остается на совести программиста.

Внимание

Арифметические операции не определены для более коротких, чем int, типов. Это означает, что если в выражении участвуют только величины типов sbyte, byte, short и ushort, перед выполнением операции они будут преобразованы в int. Таким образом, результат любой арифметической операции имеет тип не менее int.

Правила неявного преобразования иллюстрирует рис. 3.2. Если один из операндов имеет тип, изображенный на более низком уровне, чем другой, то он приводится к типу второго операнда при наличии пути между ними. Если пути нет, возникает ошибка компиляции.

Рис. 3.2. Неявные арифметические преобразования типов


Введение в исключения

При вычислении выражений могут возникнуть ошибки, например, переполнение, исчезновение порядка или деление на ноль. В C# есть механизм, который позволяет обрабатывать подобные ошибки и таким образом избегать аварийного завершения программы. Он так и называется: механизм обработки исключительных ситуаций ( исключений ).

Если в процессе вычислений возникла ошибка, система сигнализирует об этом с помощью специального действия, называемого выбрасыванием (генерированием) исключения. Каждому типу ошибки соответствует свое исключение. Поскольку C# — язык объектно-ориентированный, исключения являются классами, которые имеют общего предка — класс Exception, определенный в пространстве имен System.

Например, при делении на ноль будет сгенерировано исключение DivideByZeroException, при недостатке памяти — исключение OutOfMemoryException.

Программист может задать способ обработки исключения в специальном блоке кода, начинающемся с ключевого слова catch ("перехватить"), который будет автоматически выполнен при возникновении соответствующей исключительной ситуации. Внутри блока можно, например, вывести предупреждающее сообщение или скорректировать значения величин и продолжить выполнение программы. Если этот блок не задан, система выполнит действия по умолчанию, которые обычно заключаются в выводе диагностического сообщения и нормальном завершении программы. Обработка исключений подробно рассматривается позже.

Справочная информация: Основные операции C#

В этом разделе кратко описаны синтаксис и применение всех операций C#, кроме некоторых первичных, которые рассматриваются позже при изучении соответствующего материала.

Инкремент и декремент

Операции инкремента (++) и декремента (--) увеличивают и уменьшают операнд на единицу. Они имеют две формы записи — префиксную, когда знак операции записывается перед операндом, и постфиксную. В префиксной форме сначала изменяется операнд, а затем его значение становится результирующим значением выражения, а в постфиксной форме значением выражения является исходное значение операнда, после чего он изменяется.

Стандартные операции инкремента существуют для целых, символьных, вещественных и финансовых величин.


using System;

namespace Example_1
{
    class Program
    {
        static void Main(string[] args)
        {
            int i = 0;
            double d = 0.0;
            char ch = 'A';
            decimal m = 0.1m;
            //Операция префиксный инкремент
            Console.WriteLine("i = {0}", ++i);
            Console.WriteLine("i = {0}",i);
            Console.WriteLine("d = {0}",++d);
            Console.WriteLine("d = {0}",d);
            Console.WriteLine("ch = {0}", ++ch);
            Console.WriteLine("ch = {0}", ch);
            Console.WriteLine("m = {0}", ++m);
            Console.WriteLine("m = {0}", m);
            //Операция постфиксный инкремент
            Console.WriteLine("i = {0}", i++);
            Console.WriteLine("i = {0}", i);
            Console.WriteLine("d = {0}", d++);
            Console.WriteLine("d = {0}", d);
            Console.WriteLine("ch = {0}", ch++);
            Console.WriteLine("ch = {0}", ch);
            Console.WriteLine("m = {0}", m++);
            Console.WriteLine("m = {0}", m);
        }
    }
}

Операция new

Операция new служит для создания нового объекта. Формат операции:

new тип ( [ аргументы ] )

С помощью этой операции можно создавать объекты как ссылочных, так и значимых типов, например:

object z = new object();
// то же самое, что int i = 0;
int i = new int();

При выполнении операции new сначала выделяется необходимый объем памяти (для ссылочных типов в хипе, для значимых — в стеке), а затем вызывается так называемый конструктор по умолчанию, то есть метод, с помощью которого инициализируется объект. Переменной значимого типа присваивается значение по умолчанию, которое равно нулю соответствующего типа.

Операции отрицания

Арифметическое отрицание (унарный минус – ) меняет знак операнда на противоположный. Стандартная операция отрицания определена для типов int, long, float, double и decimal. К величинам других типов ее можно применять, если для них возможно неявное преобразование к этим типам.

Логическое отрицание ( !) определено для типа bool. Результат операции — значение false, если операнд равен true, и значение true, если операнд равен false.

Поразрядное отрицание ( ~ ), часто называемое побитовым, инвертирует каждый разряд в двоичном представлении операнда типа int, uint, long или ulong.

Явное преобразование типа

Операция используется для явного преобразования величины из одного типа в другой. Это требуется в том случае, когда неявного преобразования не существует. При преобразовании из более длинного типа в более короткий возможна потеря информации. Формат операции:

( тип ) выражение

Здесь тип — это имя того типа, в который осуществляется преобразование, а выражение чаще всего представляет собой имя переменной, например:

long b = 300;
// данные не теряются
int a = (int) b;
// данные теряются
int d = (byte) a;

Умножение, деление и остаток от деления

Операция умножения ( * ) возвращает результат перемножения двух операндов. Стандартная операция умножения определена для типов int, uint, long, ulong, float, double и decimal. К величинам других типов ее можно применять, если для них возможно неявное преобразование к этим типам. Тип результата операции равен "наибольшему" из типов операндов, но не менее int.

Все возможные значения для вещественных операндов приведены в таблице 3.2. Символами х и y обозначены конечные положительные значения, символом z — результат операции вещественного умножения. Если результат слишком велик для представления с помощью заданного типа, он принимается равным значению "бесконечность", если слишком мал, он принимается за 0. NaN (not a number) означает, что результат не является числом.


Таблица 3.2. Результаты вещественного умножения

*

+y

-y

+0

-0

+∞

-∞

NaN

+x

+z

-z

+0

-0

+∞

-∞

NaN

-x

-z

+z

-0

+0

-∞

+∞

NaN

+0

+0

-0

+0

-0

NaN

NaN

NaN

-0

-0

+0

-0

+0

NaN

NaN

NaN

+∞

+∞

-∞

NaN

NaN

+∞

-∞

NaN

-∞

-∞

+∞

NaN

NaN

-∞

+∞

NaN

NaN

NaN

NaN

NaN

NaN

NaN

NaN

NaN


Операция деления ( / ) вычисляет частное от деления первого операнда на второй. Стандартная операция деления определена для типов int, uint, long, ulong, float, double и decimal. К величинам других типов ее можно применять, если для них существует неявное преобразование к этим типам. Тип результата определяется правилами преобразования, но не меньше int.

Если оба операнда целочисленные, результат операции округляется вниз до ближайшего целого числа. Если делитель равен нулю, генерируется исключение System.DivideByZeroException.

Если хотя бы один из операндов вещественный, дробная часть результата деления не отбрасывается, а все возможные значения приведены в таблице 3.3.


Таблица 3.3. Результаты вещественного деления

/

+y

-y

+0

-0

+∞

-∞

NaN

+x

+z

-z

+∞

-∞

+0

-0

NaN

-x

-z

+z

-∞

+∞

-0

+0

NaN

+0

+0

-0

NaN

NaN

+0

-0

NaN

-0

-0

+0

NaN

NaN

-0

+0

NaN

+∞

+∞

-∞

+∞

-∞

NaN

NaN

NaN

-∞

-∞

+∞

-∞

+∞

NaN

NaN

NaN

NaN

NaN

NaN

NaN

NaN

NaN

NaN

NaN


Для финансовых величин (тип decimal ) при делении на 0 и переполнении генерируются соответствующие исключения, при исчезновении порядка результат равен 0.

Операция остатка от деления ( % ) также интерпретируется по-разному для целых, вещественных и финансовых величин. Если оба операнда целочисленные, результат операции вычисляется по формуле x - (x / y) * y. Если делитель равен нулю, генерируется исключение System.DivideByZeroException.

Если хотя бы один из операндов вещественный, результат операции вычисляется по формуле x – n * y, где n — наибольшее целое, меньшее или равное результату деления х на y. Все возможные комбинации значений операндов приведены в таблице 3.4.


Таблица 3.4. Результаты вещественного остатка от деления

%

+y

-y

+0

-0

+∞

-∞

NaN

+x

+z

-z

NaN;

NaN

x

x

NaN

-x

-z

+z

NaN

NaN

-x

-x

NaN

+0

+0

+0

NaN

NaN

+0

+0

NaN

-0

-0

-0

NaN

NaN

-0

-0

NaN

+∞

NaN

NaN

NaN

NaN

NaN

NaN

NaN

-∞

NaN

NaN

NaN

NaN

NaN

NaN

NaN

NaN

NaN

NaN

NaN

NaN

NaN

NaN

NaN


Для финансовых величин (тип decimal ) при получении остатка от деления на 0 и при переполнении генерируются соответствующие исключения, при исчезновении порядка результат равен 0. Знак результата равен знаку первого операнда.

Сложение и вычитание

Операция сложения ( + ) возвращает сумму двух операндов. Стандартная операция сложения определена для типов int, uint, long, ulong, float, double и decimal. К величинам других типов ее можно применять, если для них существует неявное преобразование к этим типам. Тип результата операции равен "наибольшему" из типов операндов, но не менее int.

Если оба операнда целочисленные или типа decimal и результат операции слишком велик для представления с помощью заданного типа, генерируется исключение System.OverflowException.

Все возможные значения для вещественных операндов приведены в таблице 3.5.


Таблица 3.5. Результаты вещественного сложения

+

y

+0

-0

+∞

-∞

NaN

x

z

x

x

+∞

-∞

NaN

+0

y

+0

+0

+∞

-∞

NaN

-0

y

+0

-0

+∞

-∞

NaN

+∞

+∞

+∞

+∞

+∞

NaN

NaN

-∞

-∞

-∞

-∞

NaN

-∞

NaN

NaN

NaN

NaN

NaN

NaN

NaN

NaN


Операция вычитания ( - ) возвращает разность двух операндов. Стандартная операция вычитания определена для типов int, uint, long, ulong, float, double и decimal. К величинам других типов ее можно применять, если для них существует неявное преобразование к этим типам. Тип результата операции равен "наибольшему" из типов операндов, но не менее int.

Если оба операнда целочисленные или типа decimal и результат операции слишком велик для представления с помощью заданного типа, генерируется исключение System.OverflowException.

Все возможные значения результата вычитания для вещественных операндов приведены в таблице 3.6. Символами х и y обозначены конечные положительные значения, символом z — результат операции вещественного вычитания. Если х и y равны, результат равен положительному нулю. Если результат слишком велик для представления с помощью заданного типа, он принимается равным значению "бесконечность" с тем же знаком, что х - y, если слишком мал, он принимается за 0 с тем же знаком, что х - y.


Таблица 3.6. Результаты вещественного вычитания

-

y

+0

-0

+∞

-∞

NaN

x

z

x

x

-∞

+∞

NaN

+0

-y

+0

+0

-∞

+∞

NaN

-0

-y

-0

+0

-∞

+∞

NaN

+∞

+∞

+∞

+∞

NaN

+∞

NaN

-∞

-∞

-∞

-∞

-∞

NaN

NaN

NaN

NaN

NaN

NaN

NaN

NaN

NaN


Операции сдвига

Операции сдвига ( << и >> ) применяются к целочисленным операндам. Они сдвигают двоичное представление первого операнда влево или вправо на количество двоичных разрядов, заданное вторым операндом.

При сдвиге влево ( << ) освободившиеся разряды обнуляются. При сдвиге вправо ( >> ) освободившиеся биты заполняются нулями, если первый операнд беззнакового типа, и знаковым разрядом в противном случае. Стандартные операции сдвига определены для типов int, uint, long и ulong.


using System;

namespace Examle_2
{
    class Program
    {
        static void Main(string[] args)
        {
            int i = 5;
            //Сдвиг влево
            Console.WriteLine("{0} << 1 = {1}", i, i << 1);
            Console.WriteLine("{0} << 2 = {1}", i,  i << 2);
            int b = 16;
            //Сдвиг вправо
            Console.WriteLine("{0} >> 1 = {0}", b, b >> 1);
            Console.WriteLine("{0} >> 2 = {0}", b, b >> 2);
        }
    }
}


Операции отношения и проверки на равенство

Операции отношения ( <, <=, >, >=, ==, != ) сравнивают первый операнд со вторым. Операнды должны быть арифметического типа. Результат операции — логического типа, равен true или false. Правила вычисления результатов приведены в таблице 3.7.


Таблица 3.7. Результаты операций отношения

Операция

Результат

x == y

true, если x равно y, иначе false

x != y

true, если x не равно y, иначе false

x < y

true, если x меньше y, иначе false

x > y

true, если x больше y, иначе false

x <= y

true, если x меньше или равно y, иначе false

x >= y

true, если x больше или равно y, иначе false


Поразрядные логические операции

Поразрядные логические операции ( &, |, ^ ) применяются к целочисленным операндам и работают с их двоичными представлениями. При выполнении операций операнды сопоставляются побитно (первый бит первого операнда с первым битом второго, второй бит первого операнда со вторым битом второго, и т д.). Стандартные операции определены для типов int, uint, long и ulong.

При поразрядной конъюнкции ( & ), бит результата равен 1 только тогда, когда соответствующие биты обоих операндов равны 1.

При поразрядной дизъюнкции ( | ), бит результата равен 1 тогда, когда соответствующий бит хотя бы одного из операндов равен 1.

При поразрядном исключающем ИЛИ ( ^ ) бит результата равен 1 только тогда, когда соответствующий бит только одного из операндов равен 1.

Условные логические операции

Условные логические операции И ( && ) и ИЛИ ( || ) чаще всего используются с операндами логического типа. Результатом логической операции является true или false. Операции вычисляются по сокращенной схеме.

Результат операции логическое И имеет значение true, только если оба операнда имеют значение true. Результат операции логическое ИЛИ имеет значение true, если хотя бы один из операндов имеет значение true. Если значения первого операнда достаточно, чтобы определить результат операции, второй операнд не вычисляется.

Условная операция

Условная операция ( ? :) имеет три операнда. Ее формат:

операнд_1 ? операнд_2 : операнд_3

Первый операнд — выражение, для которого существует неявное преобразование к логическому типу. Если результат вычисления первого операнда равен true, то результатом условной операции будет значение второго операнда, иначе — третьего операнда. Вычисляется всегда либо второй операнд, либо третий. Их тип может различаться.

Тип результата операции зависит от типа второго и третьего операндов. Если операнды одного типа, он и становится типом результата операции.

Операции присваивания

Операции присваивания ( =, +=, -=, *= и т. д.) задают новое значение переменной. Эти операции могут использоваться в программе как законченные операторы.

Формат операции простого присваивания ( = ):

еременная = выражение

Механизм выполнения операции присваивания такой: вычисляется выражение и его результат заносится в память по адресу, который определяется именем переменной, находящейся слева от знака операции. То, что ранее хранилось в этой области памяти, теряется. Примеры операторов присваивания:

a = b + c / 2;
x = 1;
x = x + 0.5;

Для правого операнда операции присваивания должно существовать неявное преобразование к типу левого операнда. Например, выражение целого типа можно присвоить вещественной переменной, потому что целые числа являются подмножеством вещественных, и информация при таком присваивании не теряется.

вещественная_переменная = целое_выражение;

Результатом операции присваивания является значение, записанное в левый операнд. Тип результата совпадает с типом левого операнда.

В сложных операциях присваивания ( +=, *=, /= и т п.) при вычислении выражения, стоящего в правой части, используется значение из левой части. Например, при сложении с присваиванием ко второму операнду прибавляется первый, и результат записывается в первый операнд, то есть выражение a += b является более компактной записью выражения a = a + b.

Результатом операции сложного присваивания является значение, записанное в левый операнд.

Операции присваивания правоассоциативны, то есть выполняются справа налево, в отличие от большинства других операций ( a = b = c означает a = (b = c) ).

Вопросы и задания для самостоятельной работы студента

1. Где можно описывать переменные? Что входит в описание переменной?

2. Что происходит при использовании в выражении операндов различных типов? Приведите примеры.

3. Перечислите операции языка C#, сгруппировав их по приоритетам.

4. Что такое NaN? В каких операциях NaN является результатом?

5. К операндам какого типа применимы операции сдвига?

6. Что такое исключительные ситуации?

7. Опишите принципы обработки исключительных ситуаций.